#include "log_module.h"
#include <pthread.h>
// 日志文件指针，默认指向 NULL
static FILE *log_file = NULL;

// 标志位，指示是否启用文件输出，默认不启用
static int use_file_output = 0;

// 文件日志的最低输出等级，默认值为当前的日志等级
static int file_log_level = CURRENT_LOG_LEVEL;

// 日志输出到文件时是否同时输出到控制台，默认true：同时输出到控制台
static bool file_and_stdout = true;

pthread_mutex_t lock; // 互斥锁

// 获取当前时间戳，精度由宏 LOG_TIMESTAMP_LEVEL 控制
void get_timestamp(char *timestamp) {
    if (timestamp == NULL) {
        fprintf(stderr, "错误：get_timestamp 函数传入的 timestamp 参数为空指针\n");
        return;  // 防止传入 NULL 指针
    }

    struct timespec ts;
    struct tm tm_info;
    time_t now = time(NULL);
    localtime_r(&now, &tm_info);

    // 根据宏定义 LOG_TIMESTAMP_LEVEL 控制显示的精度
    if (LOG_TIMESTAMP_LEVEL >= 7) {
        // 显示年
        strftime(timestamp, 20, "%Y-", &tm_info);
    }
    if (LOG_TIMESTAMP_LEVEL >= 6) {
        // 显示月
        strftime(timestamp + strlen(timestamp), 3, "%m-", &tm_info);
    }
    if (LOG_TIMESTAMP_LEVEL >= 5) {
        // 显示日
        strftime(timestamp + strlen(timestamp), 3, "%d ", &tm_info);
    }
    if (LOG_TIMESTAMP_LEVEL >= 4) {
        // 显示小时
        strftime(timestamp + strlen(timestamp), 6, "%H:", &tm_info);
    }
    if (LOG_TIMESTAMP_LEVEL >= 3) {
        // 显示分钟
        strftime(timestamp + strlen(timestamp), 6, "%M:", &tm_info);
    }
    if (LOG_TIMESTAMP_LEVEL >= 2) {
        // 显示秒
        strftime(timestamp + strlen(timestamp), 6, "%S", &tm_info);
    }
    if (LOG_TIMESTAMP_LEVEL >= 1) {
        // 显示毫秒
        clock_gettime(CLOCK_REALTIME, &ts);
        char ms[10];
        snprintf(ms, sizeof(ms), ".%03ld", ts.tv_nsec / 1000000);  // 获取毫秒
        strcat(timestamp, ms);
    }
}

// 设置日志输出到文件的路径和日志级别
void log_set_file_output(const char *file_path, int level, bool is_stdout) {
    if (file_path == NULL) {
        fprintf(stderr, "错误：log_set_file_output 函数传入的 file_path 参数为空指针\n");
        return;  // 防止传入 NULL 指针
    }

    // 如果已经打开文件，先关闭
    if (log_file) {
        fclose(log_file);
        log_file = NULL;  // 确保清空指针
    }
#if CLEAR_OLD_LOG
    // 先以 "w" 模式打开文件，清空内容，如果文件不存在，则创建文件
    log_file = fopen(file_path, "w");
    if (log_file == NULL) {
        fprintf(stderr, "错误：创建日志文件 %s 失败\n", file_path);
        return;
    }
    fclose(log_file);  // 关闭文件，清空内容完成
#endif
    // 打开文件以追加模式写入, 如果文件不存在，则创建文件
    log_file = fopen(file_path, "a");
    if (log_file) {
        // 成功打开文件，启用文件输出
        use_file_output = 1;
        file_log_level = level;  // 设置文件日志等级
    } else {
        // 文件打开失败，输出错误提示到标准错误并继续使用标准输出
        fprintf(stderr, "错误：无法打开日志文件：%s\n", file_path);
        use_file_output = 0;
        file_log_level = CURRENT_LOG_LEVEL; // 恢复为默认日志等级
    }
    file_and_stdout = is_stdout;
}

// 禁用文件日志输出，日志将只输出到控制台
void log_disable_file_output() {
    // 先检查文件指针是否有效
    if (log_file) {
        fclose(log_file);  // 关闭文件
        log_file = NULL;   // 确保指针清空
    } else {
        fprintf(stderr, "错误：尝试关闭一个空的日志文件指针\n");
    }
    use_file_output = 0;  // 禁用文件输出
    file_log_level = CURRENT_LOG_LEVEL;  // 恢复为默认日志等级
}

// 核心日志打印函数
void log_print(int level, const char *file, const char *func, int line, const char *type, const char *color, const char *fmt, ...) {
    if (file == NULL) {
        fprintf(stderr, "错误：log_print 函数传入的 file 参数为空指针\n");
        return;  // 防止传入 NULL 指针
    }
    if (func == NULL) {
        fprintf(stderr, "错误：log_print 函数传入的 func 参数为空指针\n");
        return;  // 防止传入 NULL 指针
    }
    if (fmt == NULL) {
        fprintf(stderr, "错误：log_print 函数传入的 fmt 参数为空指针\n");
        return;  // 防止传入 NULL 指针
    }

    // 判断是否同时输出到文件和控制台
    int effective_level = use_file_output ? file_log_level : CURRENT_LOG_LEVEL;
    if (level < effective_level) {
        return; // 如果日志等级低于有效等级，则不打印日志
    }

    // 获取当前时间戳
    char timestamp[50] = {0};
    get_timestamp(timestamp);

    va_list args;
    va_start(args, fmt);

    // 获取文件名（去掉路径部分）
    const char *filename = strrchr(file, '/');
    if (filename) {
        filename++; // 跳过 '/'
    } else {
        filename = file; // 没有路径时使用原始文件名
    }

    // 判断输出目标是标准输出还是文件
    FILE *output = use_file_output ? log_file : stdout;
    if (output == NULL) {
        fprintf(stderr, "错误：无法选择有效的输出目标\n");
        va_end(args);
        return;
    }
    pthread_mutex_lock(&lock); // 上锁
    // 打印时间戳、文件名、函数名、行号、日志类型以及自定义内容
    if (file_and_stdout && use_file_output) {
        // 同时输出到控制台
        fprintf(output, "%s %s[%s] %s:%s:%d: ", timestamp, color, type, filename, func, line);
        fprintf(stdout, "%s %s[%s] %s:%s:%d: ", timestamp, color, type, filename, func, line);
        vfprintf(output, fmt, args);
        vfprintf(stdout, fmt, args);
        fprintf(output, "%s", COLOR_RESET); // 重置颜色
        fprintf(stdout, "%s", COLOR_RESET); // 重置颜色
    }
    else
    { 
        fprintf(output, "%s %s[%s] %s:%s:%d: ", timestamp, color, type, filename, func, line);
        vfprintf(output, fmt, args);
        fprintf(output, "%s", COLOR_RESET); // 重置颜色
    }
    pthread_mutex_unlock(&lock); // 上锁
    va_end(args);

    // 如果启用了文件输出，确保内容被及时写入文件
    if (use_file_output && log_file) {
        fflush(output);  // 刷新文件输出缓冲区，确保内容写入
    }
}
